/**
 * Copyright 2012 Pablo Danilo Aliscioni
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package ar.com.pabloaliscioni.cloud.core.storage.providers.azure;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.Set;

import ar.com.pabloaliscioni.cloud.api.exceptions.ConfigurationException;
import ar.com.pabloaliscioni.cloud.api.exceptions.ServiceException;
import ar.com.pabloaliscioni.cloud.core.configuration.ProviderConfiguration;
import ar.com.pabloaliscioni.cloud.core.storage.BaseStorageClient;

import com.microsoft.windowsazure.services.blob.client.BlobContainerPermissions;
import com.microsoft.windowsazure.services.blob.client.BlobContainerPublicAccessType;
import com.microsoft.windowsazure.services.blob.client.CloudBlob;
import com.microsoft.windowsazure.services.blob.client.CloudBlobClient;
import com.microsoft.windowsazure.services.blob.client.CloudBlobContainer;
import com.microsoft.windowsazure.services.blob.client.CloudBlockBlob;
import com.microsoft.windowsazure.services.blob.client.ListBlobItem;
import com.microsoft.windowsazure.services.core.storage.CloudStorageAccount;
import com.microsoft.windowsazure.services.core.storage.StorageException;

/**
 * Implementation of the IStorageClient interface for Azure Blob Storage service
 * 
 * @author Pablo Danilo Aliscioni
 * @since 1.0
 */
public class AzureBlobStorageClient extends BaseStorageClient
{
	private String connectionString;
	
	private CloudBlobClient serviceClient;

	@Override
	public void configure(ProviderConfiguration providerConfiguration)
	{
		try
		{
			this.setProviderName(providerConfiguration.getName());
			
			connectionString = providerConfiguration.getConfigurationValue("Connection-String");
		}
		catch (Exception e)
		{
			throw new ConfigurationException(e);
		}
	}
	
	private CloudBlobClient getServiceClient()
	{
		try
		{
			if(serviceClient == null)
			{
				CloudStorageAccount storageAccount = CloudStorageAccount.parse(connectionString);
				
				serviceClient = storageAccount.createCloudBlobClient();
			}
			
			return serviceClient;
		}
		catch (Exception e)
		{
			throw new ConfigurationException(e);
		}
	}
	
	// API Implementation
	
	@Override
	public boolean containerExists(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			return blobContainer.exists();
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void createContainer(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			blobContainer.create();
			
			BlobContainerPermissions permissions = new BlobContainerPermissions();
			
			permissions.setPublicAccess(BlobContainerPublicAccessType.OFF);
			blobContainer.uploadPermissions(permissions);
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void deleteContainer(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			blobContainer.delete();
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public void deleteFile(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			CloudBlockBlob blob = blobContainer.getBlockBlobReference(URLEncoder.encode(fileName,"UTF-8"));
			
			blob.delete();
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public boolean fileExists(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			CloudBlockBlob blob = blobContainer.getBlockBlobReference(URLEncoder.encode(fileName,"UTF-8"));
			
			return blob.exists();
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public byte[] getFileContent(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			CloudBlockBlob blob = blobContainer.getBlockBlobReference(URLEncoder.encode(fileName,"UTF-8"));
			
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			
			blob.download(byteArrayOutputStream);
			
			return byteArrayOutputStream.toByteArray();
		}
		catch (URISyntaxException | StorageException | IOException e)
		{
			throw new ServiceException(e);
		}
	}

	@Override
	public String getFileContentType(String containerName, String fileName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			CloudBlockBlob blob = blobContainer.getBlockBlobReference(URLEncoder.encode(fileName,"UTF-8"));
			
			blob.downloadAttributes();
			
			return blob.getProperties().getContentType();
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public Set<String> listContainers()
	{
		try
		{
			Set<String> containers = new HashSet<String>();
			
			for (CloudBlobContainer item : getServiceClient().listContainers())
			{
				containers.add(item.getName());
			}
			
			return containers;
		}
		catch (Exception e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public Set<String> listFiles(String containerName)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			Set<String> files = new HashSet<String>();
			
			for (ListBlobItem item : blobContainer.listBlobs())
			{
				files.add(((CloudBlob)item).getName());
			}
			
			return files;
		}
		catch (URISyntaxException | StorageException | UnsupportedEncodingException e)
		{
			throw new ServiceException(e);
		}
	}

	@Override
	public void saveFile(String containerName, String fileName, byte[] fileContent)
	{
		saveFile(containerName, fileName, fileContent, "application/octet-stream");
	}
	
	@Override
	public void saveFile(String containerName, String fileName, byte[] fileContent, String contentType)
	{
		if(!isValidContainerName(containerName))
			throw new IllegalArgumentException("Invalid container name");
		
		if(!isValidFileName(fileName))
			throw new IllegalArgumentException("Invalid file name");
		
		if(fileContent == null)
			throw new IllegalArgumentException("Invalid file content");
		
		if(contentType == null || contentType.equals("") || !contentType.contains("/"))
			throw new IllegalArgumentException("Invalid content type");
		
		try
		{
			CloudBlobContainer blobContainer = getServiceClient().getContainerReference(URLEncoder.encode(containerName,"UTF-8"));
			
			CloudBlockBlob blob = blobContainer.getBlockBlobReference(URLEncoder.encode(fileName,"UTF-8"));
			
			blob.getProperties().setContentType(contentType);
			
			blob.upload(new ByteArrayInputStream(fileContent), fileContent.length);
		}
		catch (URISyntaxException | StorageException | IOException e)
		{
			throw new ServiceException(e);
		}
	}
	
	@Override
	public boolean isValidContainerName(String containerName)
	{
		if (containerName == null || containerName.length() < 3 || containerName.length() > 63 || 
				containerName.startsWith("-") || containerName.endsWith("-") ||containerName.contains("--") || 
				containerName.contains(".") || containerName.contains("_") || containerName.contains("!") || 
				containerName.contains("@") || containerName.contains("#") || containerName.contains("$") ||
				containerName.contains("?") || containerName.contains("+") || containerName.contains(" "))
            return false;
		
		return true;
	}
    
	@Override
	public boolean isValidFileName(String fileNname)
	{
		if (fileNname == null || fileNname.length() == 0 || fileNname.length() > 1024 ||
				fileNname.endsWith(".") || fileNname.endsWith("/"))
			return false;
		
		return true;
	}
}
